//
//  ViewController.swift
//  swift-01
//
//  Created by China Dragon on 2023/8/4.
//
/*
 oc中的 alloc init 在swift 对应的就是() 构造函数 创建并初始化对象
 */
import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        //顺序是从 demo1 开始 到最后的函数
        demo1()
//        demo5()
//        switchdemo()
//        stringdemo()
//        stringdemo2()
//        fordemo()
//        arraydemo()
//        dict_demo()
//        testMethod()
        test_bibao()
        loadData {
            print("请求成功 回调更新UI")
            //请求成功 回调更新UI
        }
        
        loadData2 { responseData in
            print("请求成功 回调更新UI \(responseData)")
            //请求成功 回调更新UI 网络数据
        }
        
        loadData3()
    }

    
    //‘尾’随闭包
    func loadData3(){
       //1.闭包参数是函数的最后一个参数 -> 末尾
       //2.函数的 ）结束，可以省略
       //3.简化闭包，如果没有参数，没有返回值，可以省略
        //https://blog.csdn.net/u011193452/article/details/123819129
        
        /*
         Swift 中的多尾随闭包
         https://www.jianshu.com/p/6ebcd93e4f13
         */

        DispatchQueue.global().async {
            print("请求1 \(Thread.current)")
            DispatchQueue.main.async {
                print("请求1 主线程")
            }
        }
        
        DispatchQueue.global().async(execute: {() -> Void in
            print("请求2 \(Thread.current)")
            DispatchQueue.main.async(execute: {() -> Void in
                print("请求2 主线程")
            })
        })
        
        DispatchQueue.global().async(execute: {
            print("请求3 \(Thread.current)")
            DispatchQueue.main.async(execute: {
                print("请求3 主线程")
            })
        })
        
        DispatchQueue.global().async(){
            print("请求4 \(Thread.current)")
            DispatchQueue.main.async(){
                print("请求4 主线程")
            }
        }

        //尾随闭包 https://blog.csdn.net/qq_15623599/article/details/89020047
        let str1 = addStr(s1: "hello", s2: "world", closure: {(c1: String, c2:String) -> (String) in return (c1) + (c2)})
        //简化
        let str2 = addStr(s1: "hello", s2: "world", closure: {(c1, c2) -> (String) in  (c1) + (c2)})
        let str3 = addStr(s1: "hello", s2: "world", closure: {($0) + ($1)})
        
        //如果闭包函数过长，放在括号内当作函数的参数时 不好阅读，可以将其移出，用{}放置于函数右侧
        let str4 = addStr(s1: "hello", s2: "world"){($0) + ($1)}
        print("str1 = \(str1)")
        print("str2 = \(str2)")
        print("str3 = \(str3)")
        print("str4 = \(str4)")
        /*
         str1 = helloworld
         str2 = helloworld
         str3 = helloworld
         str4 = helloworld
         */
      
    }
    
    
    //尾随闭包 https://blog.csdn.net/qq_15623599/article/details/89020047
    func addStr(s1: String, s2: String, closure:(String, String) -> String) -> String {
        return closure(s1, s2)
    }
    
    
    //无传惨，有返回值
    func loadData2(finished: @escaping(_ responseData: String) -> ()){
        DispatchQueue.global().async {
            print("耗时操作 \(Thread.current)")
            //通常加载数据...
            //异步完成之后,通过 block 回调
            DispatchQueue.main.async {
                print("完成 \(Thread.current)")
                //执行 finished 回调
                finished("网络数据")

            }
        }
    }
    
    //MARK: - 必包应用场景 异步加载网络数据
    func loadData(finished: @escaping() -> ()){
        /*
         swift @escaping
         https://www.jianshu.com/p/7c5ff2608c83
         在swift2中，有@noescape属性，对应非逃逸闭包，也就是在return之前就被调用，因为闭包在方法调用完成之后就会销毁，不会存在强引用，因此非逃逸闭包中不需要做weak self操作。

         在swift3中去掉了@noescape，引入了@escaping，并给将所有的闭包默认作为非逃逸闭包。

         
         */
        //block 必包应用
        //Swift闭包 对比 OC block回调异步加载
        //https://blog.csdn.net/u011193452/article/details/123819129
        
        DispatchQueue.global().async {
            print("耗时操作 \(Thread.current)")
            //通常加载数据...
            //异步完成之后,通过 block 回调
            DispatchQueue.main.async {
                print("完成 \(Thread.current)")
                //执行 finished 回调
                finished()

            }
        }
        
    }
    
    func test_bibao(){
        bibao1()
        bibao2()
    }
    
    func testMethod(){
        let r1 = sum(y: 2)
        let r2 = sum(x: 3, y:2)
        print("r1 = \(r1), r2 = \(r2)")
        //r1 = 12, r2 = 5
    }
    
    func bibao2(){
        /*
        必包的所有代码[参数、返回值、被执行的代码]都在{}中
         
         必包格式
         {
            (参数1，参数2) -> 返回值 in
         }
         */
        
        let sumc = {
            (x: Int, y: Int) -> Int
            in
            return x + y
            
        }
        
        let demo = {
            
            print("必包 没有 参数，没有返回值")
        }
        
        let r = sumc(4, 5)
        print("r = \(r)")
        
    }
    
    //MARK: - 必包的定义
    func bibao1(){
        /*
         必包：类似OC中的 block
         block
         是C语言的
         是一组预先准备好的代码，在需要的时候执行
         可以当做参数传递
         如果出现self 需要注意循环引用
         
         在 swift 中函数本身可以当做参数传递
         
         */
        
        let sumc = sum // 定义函数常量
        let r = sumc(10, 20)
        print("r = \(r)")
    }
    
    /*
     没有返回值的情况
     1、什么都不写
     2、Void
     3、()
     
     三种类型 在必包中会使用到
     */
    
    func noResult1(){
        
    }
    
    func noResult2() -> Void{
        
    }
    
    func noResult3() -> (){
        
    }
    
    //MARK: - 函数
    func sum(x: Int = 10, y: Int) -> Int{
        return x + y
    }
    
    //MARK: - 字典的演练
    func dict_demo(){
        /*
            OC 中定义字典使用{}
            Swift 中仍然使用[], 是通过值对
         */
        
        //let 不可变  var 可变
        let dict = ["name":"汪汪", "age":2] as [String : Any]
        var dict2 = ["name":"汪汪", "age":2] as [String : Any]
        
        //如果key 不存在，则新增， 如果存在，覆盖
        dict2["name"] = "飞虎"
        dict2["title"] = "新闻"
        print(dict)
        print(dict2)
        //["name": "汪汪", "age": 2]
        //["age": 2, "title": "新闻", "name": "飞虎"]
        
        //前是key 后面是 value
        for(k, v) in dict2{
            print("KEY =  \(k) ---- VALUE =  \(v)")
        }
        /*
         KEY =  title ---- VALUE =  新闻
         KEY =  name ---- VALUE =  飞虎
         KEY =  age ---- VALUE =  2

         */
        
        //字典的合并
        var dict3 = ["name":"龙", "age":20, "n":"test"] as [String : Any]
        //将 dict 合并到 dict3
        for (k,v) in dict{
            dict3[k] = v
        }
        print(dict3)
        //["n": "test", "name": "汪汪", "age": 2]
        
    }
    
    // OC 里使用 #pargra mark 预编译， swift 里不支持，swift 里使用 //MARK: -
    // MARK: - 数组的演练
    func arraydemo(){
        /*
          let 定义的是不可变
          var 定义的是可变的
         */
        let array = ["test1","test2"]
        let array2 = ["test1","test2",20, NSValue(cgRect: CGRect(x: 0, y: 0, width: 200, height: 100))] as [Any]
        var array3 = ["ar1","ar2"]
        var array4: [Any] = ["t1","t2"]
        //修改
//        array3[0] = "test_1"
        //删除
//        array3.remove(at: 1)
       //添加元素
//        array3.append("ar3")
        
//        array.append("fw") 不可变
        array4.append(18)
//        print(array)
        array3.removeAll(keepingCapacity: true)//删除所有元素，并且 保留容量
//        print("array3 数组容量: \(array3.capacity)")
//        print("-----------")
//        print(array2)
//        print(array4)
//        ["test1", "test2"]
//        -----------
//        ["test1", "test2", 20, NSRect: {{0, 0}, {200, 100}}]
//        ["t1", "t2", 18]
        
        
        //遍历
//        for name in array {
//            print(name)
//        }
//
        // 数组容量
        var users:[String]
        users = [String]()
        for i in 0 ..< 16{
            users.append("s \(i)")
            //当插入元素时，如果容量不够，会在当前容量基础上 * 2，方便后续的添加元素
            print(users[i] + " - 容量 \(users.capacity)")
        }
        
        print(users)
        /*
         s 0 - 容量 1
         s 1 - 容量 2
         s 2 - 容量 4
         s 3 - 容量 4
         s 4 - 容量 8
         s 5 - 容量 8
         s 6 - 容量 8
         s 7 - 容量 8
         s 8 - 容量 16
         s 9 - 容量 16
         s 10 - 容量 16
         s 11 - 容量 16
         s 12 - 容量 16
         s 13 - 容量 16
         s 14 - 容量 16
         s 15 - 容量 16
         ["s 0", "s 1", "s 2", "s 3", "s 4", "s 5", "s 6", "s 7", "s 8", "s 9", "s 10", "s 11", "s 12", "s 13", "s 14", "s 15"]

         */
        
        //定义一个空的存放字符串数组
        var testarray = [String]()
        
        
        //合并数组
        var arr4 = ["t1","t2"]
        //将 array 合并到 arr4， 注意合并到时候 数据类型保存一致
        arr4 += array;
        print(arr4)
    }
    
    func fordemo(){
        
        //定义范围 不包含右边的
        for i in 0 ..< 9{// 0 ~ 8, ..< 不包含右边的数字
            print(i)
        }
        
        print("-------")
        //定义范围 包含
        for k in 0...9{// 0 ~ 9
            print(k)
        }
    }
    
    //swift 之 字符串的用法 https://blog.csdn.net/qq_41240287/article/details/123746407
    func stringdemo2(){
        // 字符串的子串
        let str:String = "hello world"
        let s1 = (str as NSString).substring(to: 5)
        let s2 = (str as NSString).substring(with: NSRange(location: 2,length: 6))
        print(s1)
        //hello
        print(s2)
        //llo wo
        
        //指定范围 ..<
        let startIndex = str.startIndex
        let endIndex = str.endIndex
        let range = startIndex ..< endIndex
        let s3 = str.substring(with: range)
        print(s3)
        //hello world

    }
    
    func stringdemo(){
        /*
         字符串
         swift 中是 String 类型
         结构体、量级更轻，苹果推荐使用，
         支持直接遍历
         OC 中是 NSString 类型
         继承自 NSObject 的对象
         
         */
        
        let name = "test"
        let age = 20
        let center = view.center
        // 字符串的拼接：\(变量名) 方式
        print("姓名：\(name) ， 年龄：\(age), center = \(center)")
        //姓名：test ， 年龄：20, center = (196.5, 426.0)
        
        let str = "hello world "
        for c in str{
            //            print(c)// 打印出每个字母
        }
        
        let txt = "你好世界hello"
        //一个中文的 UTF 对应3个字节，返回 C 语言的字符串长度
        print(txt.lengthOfBytes(using: String.Encoding.utf8))
        //17
        //返回字符串中的字符数量 - 字符串的长度
        print(txt.count)
        //9
        
        
        let h = 6
        let m = 20
        let s = 30
        print("\(h):\(m):\(s)")
        // 6:20:30
        
        //使用  String(format: xxx) 单独格式化
        let dateString = String(format: "%02d:%02d:%02d",arguments:  [h,m,s])
        print("datestring is \(dateString)")
        //        datestring is 06:20:30
        
        let dateString2 = String(format: "%02d:%02d:%02d",h,m,s)
        print("dateString2 is \(dateString2)")
        //        dateString2 is 06:20:30
    }
    
    func switchdemo(){
        let num = 10
        switch num{
        case 10, 9:
            print("good")
        case 5 where num > 1:
            print("pass")
        case 0:
            print("no")
        default:
            print("未知")
        }
    }
    
    func demo5(){
        let x = 10
        /*
         没有（），必须有{}
         条件判断语句：C语言中有非零即真，但是 swift 只有 true 、false
         */
        if x > 20 {
            
        }else{
            
        }
        
        //三目 在swift用的比较多
        x > 20 ? print("成立") : print("不成立")
        
        
        
        let oName: String? = "test"
        let oAge: Int? = 10
        if oName != nil && oAge != nil{
            print("姓名：" + oName! + "，年龄："+String(oAge!))
        }
        // if let 用法, 可以设置数值，进入分支后，name 可以保证一定有值
        if let name = oName{
            print("姓名："+name)
        }
        
        /*
         if let"和"guard let
         http://www.manongjc.com/detail/51-rhyvshjkgkwlvmc.html
         https://www.jianshu.com/p/1ea640a11e6a
         
         if let swift 1.0推出的语法
         guard let swift 2.0推出的语法
         
         guard let 和 if let 相反命令
         guard let 可以减少嵌套层
         
         */
        //多值的设置，使用逗号分隔
        if let name2 = oName, let age1 = oAge{
            print("姓名：" + name2 + "，年龄："+String(age1))
        }
        
        // where 关键字 https://www.jianshu.com/p/8f47dfa87b23    http://www.manongjc.com/detail/52-vnjhpuxxuvuozql.html
        let datas = [1,2,3,4,5,6,7,8,9,10]
        for item in datas where item > 4 {
            print(item)
        }
        
        guard let g_name = oName else{
            print("g_name 为 nil")
            return
        }
        
        guard let g_age = oAge else{
            print("g_age 为 nil")
            return
        }
        
        //此时的g_name，g_age 一定有值
        print("姓名：" + g_name + "，年龄："+String(g_age))
    }
    
    func setValue1(item : [String]?) {
        guard let item = item, item.count > 4 else { return }
    }
//    Swift4.0以后用，代替where
//    func setValue2(item : [String]?) {
//        guard let item = item where item.count > 4 else { return }
//    }
    
    func demo4(){
        let urlString = "https://www.baidu.com"
        //        let urlString = "https://www.baidu.com/中文"
        let url = NSURL(string: urlString);
        print("url = ",url)
        //url =  Optional(https://www.baidu.com)
        //url =  nil
        
        if(url != nil){
            let request = NSURLRequest(url: url! as URL)
            print("request = ",request)
            //request =  <NSURLRequest: 0x600000fa00c0> { URL: https://www.baidu.com }
        }
        
        
    }
    
    func demo3(){
        /*
         可选项：一个变量，可以为本身的类型，可以为nil
         可选项使用 ？ 定义
         
         */
        //定义变量或常量如需指定类型
        let x: Double = 20
        let r1 = x + 1.5
        print("r1 = ",r1)
        
        // y 可以是一个整数，也可以是 nil, 如果是变量 默认的是 nil
        var y: Int? = 10
        print("y = ", y)
        //y =  Optional(10)
        
        //可选项不能直接计算
        //        print("y = ", y+20)
        
        //！- 强行解包，y 不为 nil
        print("y = ", y!+20)
        //y =  30
        
        var y2: Int?
        // ?? 运算符优先级 低 所以前面 加上（）
        print("y = ", (y2 ?? 1)+20)
        //y =  21
        
        
    }
    
    func demo2(){
        /*
         自动推导出 变量/常量类型
         swift 任何时候都不会做隐式转换
         任务两个类型不同的变量或常量 不允许直接计算
         
         整数默认的是Int -> 64位整数 long
         小数默认的是 Double -> 双精度小数， OC 中使用 CGFloat 比较多，浮点数
         
         */
        let x = 20 // 选中 x 按住 option 键 鼠标点击 x 可以查看到 x 是 Int 类型
        let y = 1.5 //选中 y 按住 option 键 鼠标点击 y 可以查看到 x 是 Double 类型
        
        //        let r = x + y // 这里不允许直接计算
        let r1 = x + Int(y)
        print("r1 = ",r1)
        //r1 =  21
        
        let r2 = Double(x) + y
        print("r2 = ",r2)
        //2 =  21.5
    }

    func demo1(){
        /*
         let 定义常量，没有默认值，必须要设置初始值
         var 定义变量，默认值 nil
         
         平时开发尽量用let, 必须做修改的时候用 var , 这样程序更安全
         */
        
        let name: String?// 定义常量的时候，如果只指定了类型，没有设置初始值，有一次机会设置初始值
        name = "test"
        //        name = "test2"
        
        //swift 语句后面没有分号，如果有，如下需要添加
        let a = 10; let  b = 20
        print(a + b)
        
        //视图实例化之后，并没有修改指针地址
        let v = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
        //只是在修改属性
        v.backgroundColor = UIColor.red
        
        view.addSubview(v)
        
        let btn = UIButton(type: UIButton.ButtonType.contactAdd)
        btn.center = view.center
        view.addSubview(btn)
        
        //添加监听方法
        //        btn.addTarget(self, action:"click", for:UIControl.Event.touchUpInside)
        
        //IOS基础控件 UIButton swift3.1 https://www.likecs.com/show-204679976.html
        btn.addTarget(self, action:#selector(ViewController.click2), for:UIControl.Event.touchUpInside)
    }
    
    /*
     如果是调用的方法是私有的方法，则必须赋予其 Objective-C 的 runtime（运行时）。即在方法名前加上 @objc：
     https://www.jianshu.com/p/556731dd0240
     */
    
    @objc func click(){
        print("按钮被点击了")
    }
    
    @objc func click2(btn: UIButton){
        print("按钮被点击了 \(btn)")
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    
}

